#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <string.h>
#include <time.h>

#define SIZE 10
//define types of character
#define FACE1 1
#define FACE2 2
#define MALE 11
#define FEMALE 12
#define KEY 14
//define types of monster
#define SPADE 6
#define CLUB 5
#define DIAMOND 4
#define HEART 3
#define WORM 15
//define types of item
#define MUSIC 14 //music symbol
#define SPEED_UP 175
#define SLOW_DOWN 174
//define types of events
#define WIN 99
#define LOSE -1
#define DIED 237
#define NOTHING 0
#define OUT_OF_MAP -2
#define GOT_KEY -3
//define types of grounds
#define BLANK 249
#define WALL1 176
#define WALL2 177
#define WALL3 178
#define WALL4 219
#define TOP_DOOR 31
#define BOT_DOOR 30
#define LEFT_DOOR 16
#define RIGHT_DOOR 17
#define WATER 247
#define HOLE 232

int MAP[SIZE][SIZE];

//define types of structure
struct PLAYER{
	int type;
	//position
	int row, col;
};
struct TARGET{
	int type;
	//position
	int row, col;
};
struct MONSTER{
	int type;
	//position
	int row, col;
};
struct MONSTERS{
	MONSTER nodes[SIZE];
	int n;
};

//global variables
int sex=-1;
//character position
int _row=-1, _col=-1;
PLAYER you;
//princess position
int _prow=-1, _pcol=-1;
PLAYER honey;
//monsters
MONSTERS mons;
int typeOfMons[4]={SPADE,CLUB,DIAMOND,WORM};
//SCORE
int _step;
char key;
bool Key=false;
int result=NOTHING;

//map function
void createMap();
void drawMap();

//display function
void printScore();
bool gameOver();

//move function
bool checkValidPos(int,int);
int moveTo(int,int);
int turnUp();
int turnDown();
int turnLeft();
int turnRight();
int checkEvents();
void moveMonster();
bool checkValidMonsterPos(int,int);

//initialize stats
void initCharacter();
void initMonster();
void buildUpLockedRoom();
void buildUpWall(int,int,int);
void initKey();
int getBlankCell();
int getTypeOfMonster();

bool runningGame();

void main()
{
	createMap();
	//drawMap();
	initCharacter();
	initMonster();
	if(runningGame())
		printf("There are errors while running! Don't worry, it will be fixed as soon as possible!\n");
	else
		printf("Thank you! See you soon!\n");
	
}

//map function
void createMap()
{
	for(int i=0; i<SIZE; i++)
	{
		for(int j=0; j<SIZE; j++)
		{
			MAP[i][j]=BLANK;
		}
	}	
	_step=0;
}
void drawMap()
{
	//unlock the target room
	if(Key)
	{
		int row, col;
		row=honey.row;
		col=honey.col;
		MAP[row-1][col]=BLANK;
		MAP[row+1][col]=BLANK;
		MAP[row][col-1]=BLANK;
		MAP[row][col+1]=BLANK;
	}
	//PLAYING screen
	printf("\t--SAVING PRINCESS--\n\n");
	printf("\t  ");
	for(int i=0; i<SIZE; i++)
	{
		printf(" %d ",i);
	}
	printf("\n");
	for(int i=0; i<SIZE; i++)
	{
		printf("\t%d  ",i);
		for(int j=0; j<SIZE; j++)
		{			
			printf("%-3c",(char)MAP[i][j]);
		}
		printf("\n");
	}
	
}

//events function
int checkEvents()
{
	switch(MAP[_row][_col])
	{
	case FEMALE:
		if(you.type==MALE) return WIN;
		else return NOTHING;
		break;
	case MALE:
		if(you.type==FEMALE) return WIN;
		else return NOTHING;
		break;
	case WORM: case SPADE: case CLUB: case DIAMOND:
		return LOSE;
		break;	
	case HEART:
		return NOTHING;
		break;
	case KEY:
		Key=true;
		return GOT_KEY;
		break;
	default:
		return NOTHING;
	}

}

//display function
void printScore()
{
	printf("\n--YOUR STATUS--\n");
	if(!Key) printf("\t?? YOU ALREADY HAVE THE KEY YET ?? LET'S FIND IT TO UNLOCK THE ROOM!!!\n");
	else printf("!\t! YOU ALREADY HAVE THE KEY !! LET'S MOVE TO THE ROOM AND SAVE YOUR HONEY!!!\n");
	printf("\t\t++ Number of Your steps: %d ++\n",_step);
}

bool gameOver()
{
	if(result==WIN || result==LOSE)
	{
		printf("\t\t   --GAME OVER--\n");
		printf("\t\t--YOU ARE ");
		if(result==WIN) printf("WINNER--\n");
		else printf("LOSER--\n");
		printf("\t\t\t++Final score: %d steps\n",_step);
		return true;	
	}
	else{		
		printScore();
		return false;
	}
}

//move function
bool checkValidPos(int x,int y)
{
	if(x<0 || y<0 || x>=SIZE || y>=SIZE) return false;
	if(MAP[x][y]==WALL1 || MAP[x][y]==WALL2 || MAP[x][y]==WALL3 || MAP[x][y]==WALL4 || MAP[x][y]==TOP_DOOR || MAP[x][y]==BOT_DOOR || MAP[x][y]==LEFT_DOOR || MAP[x][y]==RIGHT_DOOR) return false;
	return true;
}
bool checkValidMonsterPos(int x,int y)
{
	if(x<0 || y<0 || x>=SIZE || y>=SIZE) return false;
	if(MAP[x][y]==WALL1 || MAP[x][y]==WALL2 || MAP[x][y]==WALL3 || MAP[x][y]==WALL4 || MAP[x][y]==TOP_DOOR || MAP[x][y]==BOT_DOOR || MAP[x][y]==LEFT_DOOR || MAP[x][y]==RIGHT_DOOR) return false;
	if(MAP[x][y]==MALE || MAP[x][y]==FEMALE) return true;
	if(MAP[x][y]!=BLANK) return false;
	return true;
}
int moveTo(int x,int y)
{
	int e;
	if(checkValidPos(x,y))
	{
		MAP[_row][_col]=BLANK;
		_row=x; _col=y;
		//check event
		e=checkEvents();		
		if(e==WIN) MAP[_row][_col]=HEART;
		else if(e==LOSE) MAP[_row][_col]=DIED;
		else MAP[x][y]=sex?MALE:FEMALE;
		_step++;		
	}
	else e=OUT_OF_MAP;
	return e;
}
int turnUp()
{
	return moveTo(_row-1,_col);
}
int turnDown()
{
	return moveTo(_row+1,_col);
}
int turnLeft()
{
	return moveTo(_row,_col-1);
}
int turnRight()
{
	return moveTo(_row,_col+1);
}
void moveMonster()
{
	srand(time(NULL));
	int row, col;
	for(int i=0; i<mons.n; i++)
	{
		do{
			row=mons.nodes[i].row;
			col=mons.nodes[i].col;
			switch(rand()%4)
			{
			case 0: //turn up
				row--;
				break;
			case 1: //turn down
				row++;
				break;
			case 2: //turn left
				col--;
				break;
			case 3: //turn right
				col++;
				break;
			}
		}while(!checkValidMonsterPos(row,col));
		MAP[mons.nodes[i].row][mons.nodes[i].col]=BLANK;
		MAP[row][col]=mons.nodes[i].type;
		mons.nodes[i].row=row;
		mons.nodes[i].col=col;
	}
}

//initialize stats
void initCharacter()
{	
	do{
		system("cls");
		drawMap();
		printf("--YOU WANNA BE MALE(1) OR FEMALE(0)???--\n");
		printf("\t+I choose: ");
		scanf_s("%d",&sex);
		printf("--SET POSITION--\n");
		printf("\t+YOUR POSITION: ");
		scanf_s("%d%d",&_row,&_col);
		printf("\t+YOUR HONEY POSITION: ");
		scanf_s("%d%d",&_prow,&_pcol);
	}while(_row<0||_col>=SIZE||_prow<0||_pcol>=SIZE||(sex!=1&&sex!=0));
	//set information of player
	you.type=sex?MALE:FEMALE;
	you.row=_row;
	you.col=_col;
	honey.type=sex?FEMALE:MALE;
	honey.row=_prow;
	honey.col=_pcol;
	//put you and honey on map
	MAP[_row][_col]=you.type;
	MAP[_prow][_pcol]=honey.type;
	buildUpLockedRoom();
	initKey();
	printf("--DONE!--\n");
	system("pause");
}
void initMonster()
{
	do{
		system("cls");
		drawMap();
		printf("++Numbers of Monster: ");
		scanf_s("%d",&mons.n);
	}while(mons.n>getBlankCell());
	int row, col;
	for(int i=0; i<mons.n; i++)
	{		
		mons.nodes[i].type=getTypeOfMonster();
		do
		{
			srand(time(NULL));
			row=rand()%SIZE;
			col=rand()%SIZE;
		}while(MAP[row][col]!=BLANK);
		mons.nodes[i].row=row;
		mons.nodes[i].col=col;
		//put monsters on map
		MAP[mons.nodes[i].row][mons.nodes[i].col]=mons.nodes[i].type;
		//display monster and its position
		system("cls");
		drawMap();
		printf("++Monster: %d(type) [%d %d](position)\n",mons.nodes[i].type,mons.nodes[i].row,mons.nodes[i].col);
		system("pause");
	}
}
void initKey()
{
	int row,col;
	do
	{
		srand(time(NULL));
		row=rand()%SIZE;
		col=rand()%SIZE;
	}while(MAP[row][col]!=BLANK);
	MAP[row][col]=KEY;
}
int getBlankCell()
{
	int nblank=0;
	for(int i=0; i<SIZE; i++)
	{		
		for(int j=0; j<SIZE; j++)
		{			
			if(MAP[i][j]==BLANK) nblank++;
		}
	}
	return nblank;
}
int getTypeOfMonster()
{
	srand(time(NULL));
	return typeOfMons[rand()%4];
}
void buildUpWall(int row,int col,int type)
{
	MAP[row][col]=type;
}
void buildUpLockedRoom()
{
	int row, col;
	row=honey.row;
	col=honey.col;
	//left-top
	if(checkValidPos(row-1,col-1)) buildUpWall(row-1,col-1,WALL4);
	//top-door
	if(checkValidPos(row-1,col)) buildUpWall(row-1,col,TOP_DOOR);
	//right-top
	if(checkValidPos(row-1,col+1)) buildUpWall(row-1,col+1,WALL4);
	//left-bot
	if(checkValidPos(row+1,col-1)) buildUpWall(row+1,col-1,WALL4);
	//bot-door
	if(checkValidPos(row+1,col)) buildUpWall(row+1,col,BOT_DOOR);
	//right-bot
	if(checkValidPos(row+1,col+1)) buildUpWall(row+1,col+1,WALL4);
	//left-door
	if(checkValidPos(row,col-1)) buildUpWall(row,col-1,LEFT_DOOR);
	//right-door
	if(checkValidPos(row,col+1)) buildUpWall(row,col+1,RIGHT_DOOR);
}

bool runningGame()
{
	bool error=true;
	while(1)
	{
		if(key!=-32) moveMonster();
		fflush(stdin);
		key=_getch();
		switch(key)
		{
		case 'W': case 'w': case 72:
			result=turnUp();
			break;
		case 'S': case 's': case 80:
			result=turnDown();
			break;
		case 'A': case 'a': case 75:
			result=turnLeft();
			break;
		case 'D': case 'd': case 77:
			result=turnRight();
			break;
		case -32:
			continue;
			break;
		case 27: //press on ESC key
			printf("\n\t--GOOD BYE--\n");
			return true;
			break;
		default:
			printf("You've just chosen invalid key! ASCII code: %d\n",key);
			//system("pause");
		}				
		//clear old and render new map on screeen
		system("cls");
		drawMap();
		if(gameOver())
		{
			error=false;
			break;
		}
	}
	return error;
}